/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.corext.refactoring; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.IExtendedModifier; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.QualifiedType; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.TypeParameter; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.TypeNameMatch; import org.eclipse.jdt.internal.corext.dom.ASTFlattener; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor; import org.eclipse.jdt.internal.corext.dom.Selection; import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer; import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; import org.eclipse.jdt.internal.corext.util.JavaModelUtil; import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaTypeCompletionProcessor; import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; public class TypeContextChecker { private static class MethodTypesChecker { private static final String METHOD_NAME= "__$$__"; //$NON-NLS-1$ private final IMethod fMethod; private final StubTypeContext fStubTypeContext; private final List/*<ParameterInfo>*/ fParameterInfos; private final ReturnTypeInfo fReturnTypeInfo; public MethodTypesChecker(IMethod method, StubTypeContext stubTypeContext, List/*<ParameterInfo>*/ parameterInfos, ReturnTypeInfo returnTypeInfo) { fMethod= method; fStubTypeContext= stubTypeContext; fParameterInfos= parameterInfos; fReturnTypeInfo= returnTypeInfo; } public RefactoringStatus[] checkAndResolveMethodTypes() throws CoreException { RefactoringStatus[] results= new MethodTypesSyntaxChecker(fMethod, fParameterInfos, fReturnTypeInfo).checkSyntax(); for (int i= 0; i < results.length; i++) if (results[i] != null && results[i].hasFatalError()) return results; int parameterCount= fParameterInfos.size(); String[] types= new String[parameterCount + 1]; for (int i= 0; i < parameterCount; i++) types[i]= ParameterInfo.stripEllipsis(((ParameterInfo) fParameterInfos.get(i)).getNewTypeName()); types[parameterCount]= fReturnTypeInfo.getNewTypeName(); RefactoringStatus[] semanticsResults= new RefactoringStatus[parameterCount + 1]; ITypeBinding[] typeBindings= resolveBindings(types, semanticsResults, true); boolean needsSecondPass= false; for (int i= 0; i < types.length; i++) if (typeBindings[i] == null || ! semanticsResults[i].isOK()) needsSecondPass= true; RefactoringStatus[] semanticsResults2= new RefactoringStatus[parameterCount + 1]; if (needsSecondPass) typeBindings= resolveBindings(types, semanticsResults2, false); for (int i= 0; i < fParameterInfos.size(); i++) { ParameterInfo parameterInfo= (ParameterInfo) fParameterInfos.get(i); if (!parameterInfo.isResolve()) continue; if (parameterInfo.getOldTypeBinding() != null && ! parameterInfo.isTypeNameChanged()) { parameterInfo.setNewTypeBinding(parameterInfo.getOldTypeBinding()); } else { parameterInfo.setNewTypeBinding(typeBindings[i]); if (typeBindings[i] == null || (needsSecondPass && ! semanticsResults2[i].isOK())) { if (results[i] == null) results[i]= semanticsResults2[i]; else results[i].merge(semanticsResults2[i]); } } } fReturnTypeInfo.setNewTypeBinding(typeBindings[fParameterInfos.size()]); if (typeBindings[parameterCount] == null || (needsSecondPass && ! semanticsResults2[parameterCount].isOK())) { if (results[parameterCount] == null) results[parameterCount]= semanticsResults2[parameterCount]; else results[parameterCount].merge(semanticsResults2[parameterCount]); } return results; } private ITypeBinding[] resolveBindings(String[] types, RefactoringStatus[] results, boolean firstPass) throws CoreException { //TODO: split types into parameterTypes and returnType int parameterCount= types.length - 1; ITypeBinding[] typeBindings= new ITypeBinding[types.length]; StringBuffer cuString= new StringBuffer(); cuString.append(fStubTypeContext.getBeforeString()); int offsetBeforeMethodName= appendMethodDeclaration(cuString, types, parameterCount); cuString.append(fStubTypeContext.getAfterString()); // need a working copy to tell the parser where to resolve (package visible) types ICompilationUnit wc= fMethod.getCompilationUnit().getWorkingCopy(new WorkingCopyOwner() {/*subclass*/}, new NullProgressMonitor()); try { wc.getBuffer().setContents(cuString.toString()); CompilationUnit compilationUnit= new RefactoringASTParser(AST.JLS3).parse(wc, true); ASTNode method= NodeFinder.perform(compilationUnit, offsetBeforeMethodName, METHOD_NAME.length()).getParent(); Type[] typeNodes= new Type[types.length]; if (method instanceof MethodDeclaration) { MethodDeclaration methodDeclaration= (MethodDeclaration) method; typeNodes[parameterCount]= methodDeclaration.getReturnType2(); List/*<SingleVariableDeclaration>*/ parameters= methodDeclaration.parameters(); for (int i= 0; i < parameterCount; i++) typeNodes[i]= ((SingleVariableDeclaration) parameters.get(i)).getType(); } else if (method instanceof AnnotationTypeMemberDeclaration) { typeNodes[0]= ((AnnotationTypeMemberDeclaration) method).getType(); } for (int i= 0; i < types.length; i++) { Type type= typeNodes[i]; if (type == null) { String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_couldNotResolveType, BasicElementLabels.getJavaElementName(types[i])); results[i]= RefactoringStatus.createErrorStatus(msg); continue; } results[i]= new RefactoringStatus(); IProblem[] problems= ASTNodes.getProblems(type, ASTNodes.NODE_ONLY, ASTNodes.PROBLEMS); if (problems.length > 0) { for (int p= 0; p < problems.length; p++) if (isError(problems[p], type)) results[i].addError(problems[p].getMessage()); } ITypeBinding binding= handleBug84585(type.resolveBinding()); if (firstPass && (binding == null || binding.isRecovered())) { types[i]= qualifyTypes(type, results[i]); } typeBindings[i]= binding; } return typeBindings; } finally { wc.discardWorkingCopy(); } } /** * Decides if a problem matters. * @param problem the problem * @param type the current type * @return return if a problem matters. */ private boolean isError(IProblem problem, Type type) { return true; } private int appendMethodDeclaration(StringBuffer cuString, String[] types, int parameterCount) throws JavaModelException { if (Flags.isStatic(fMethod.getFlags())) cuString.append("static "); //$NON-NLS-1$ ITypeParameter[] methodTypeParameters= fMethod.getTypeParameters(); if (methodTypeParameters.length != 0) { cuString.append('<'); for (int i= 0; i < methodTypeParameters.length; i++) { ITypeParameter typeParameter= methodTypeParameters[i]; if (i > 0) cuString.append(','); cuString.append(typeParameter.getElementName()); } cuString.append("> "); //$NON-NLS-1$ } cuString.append(types[parameterCount]).append(' '); int offsetBeforeMethodName= cuString.length(); cuString.append(METHOD_NAME).append('('); for (int i= 0; i < parameterCount; i++) { if (i > 0) cuString.append(','); cuString.append(types[i]).append(" p").append(i); //$NON-NLS-1$ } cuString.append(");"); //$NON-NLS-1$ return offsetBeforeMethodName; } private String qualifyTypes(Type type, final RefactoringStatus result) throws CoreException { class NestedException extends RuntimeException { private static final long serialVersionUID= 1L; NestedException(CoreException e) { super(e); } } ASTFlattener flattener= new ASTFlattener() { public boolean visit(SimpleName node) { appendResolved(node.getIdentifier()); return false; } public boolean visit(QualifiedName node) { appendResolved(node.getFullyQualifiedName()); return false; } public boolean visit(QualifiedType node) { appendResolved(ASTNodes.asString(node)); return false; } private void appendResolved(String typeName) { String resolvedType; try { resolvedType= resolveType(typeName, result, fMethod.getDeclaringType(), null); } catch (CoreException e) { throw new NestedException(e); } this.fBuffer.append(resolvedType); } }; try { type.accept(flattener); } catch (NestedException e) { throw ((CoreException) e.getCause()); } return flattener.getResult(); } private static String resolveType(String elementTypeName, RefactoringStatus status, IType declaringType, IProgressMonitor pm) throws CoreException { String[][] fqns= declaringType.resolveType(elementTypeName); if (fqns != null) { if (fqns.length == 1) { return JavaModelUtil.concatenateName(fqns[0][0], fqns[0][1]); } else if (fqns.length > 1){ String[] keys= { BasicElementLabels.getJavaElementName(elementTypeName), String.valueOf(fqns.length)}; String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_ambiguous, keys); status.addError(msg); return elementTypeName; } } List typeRefsFound= findTypeInfos(elementTypeName, declaringType, pm); if (typeRefsFound.size() == 0){ String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_not_unique, BasicElementLabels.getJavaElementName(elementTypeName)); status.addError(msg); return elementTypeName; } else if (typeRefsFound.size() == 1){ TypeNameMatch typeInfo= (TypeNameMatch) typeRefsFound.get(0); return typeInfo.getFullyQualifiedName(); } else { Assert.isTrue(typeRefsFound.size() > 1); String[] keys= {BasicElementLabels.getJavaElementName(elementTypeName), String.valueOf(typeRefsFound.size())}; String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_ambiguous, keys); status.addError(msg); return elementTypeName; } } private static List findTypeInfos(String typeName, IType contextType, IProgressMonitor pm) throws JavaModelException { IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaProject[]{contextType.getJavaProject()}, true); IPackageFragment currPackage= contextType.getPackageFragment(); ArrayList collectedInfos= new ArrayList(); TypeNameMatchCollector requestor= new TypeNameMatchCollector(collectedInfos); int matchMode= SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE; new SearchEngine().searchAllTypeNames(null, matchMode, typeName.toCharArray(), matchMode, IJavaSearchConstants.TYPE, scope, requestor, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, pm); List result= new ArrayList(); for (Iterator iter= collectedInfos.iterator(); iter.hasNext();) { TypeNameMatch curr= (TypeNameMatch) iter.next(); IType type= curr.getType(); if (type != null) { boolean visible=true; try { visible= JavaModelUtil.isVisible(type, currPackage); } catch (JavaModelException e) { //Assume visibile if not available } if (visible) { result.add(curr); } } } return result; } } private static class MethodTypesSyntaxChecker { private final IMethod fMethod; private final List/*<ParameterInfo>*/ fParameterInfos; private final ReturnTypeInfo fReturnTypeInfo; public MethodTypesSyntaxChecker(IMethod method, List/*<ParameterInfo>*/ parameterInfos, ReturnTypeInfo returnTypeInfo) { fMethod= method; fParameterInfos= parameterInfos; fReturnTypeInfo= returnTypeInfo; } public RefactoringStatus[] checkSyntax() { int parameterCount= fParameterInfos.size(); RefactoringStatus[] results= new RefactoringStatus[parameterCount + 1]; results[parameterCount]= checkReturnTypeSyntax(); for (int i= 0; i < parameterCount; i++) { ParameterInfo info= (ParameterInfo) fParameterInfos.get(i); if (!info.isDeleted()) results[i]= checkParameterTypeSyntax(info); } return results; } private RefactoringStatus checkParameterTypeSyntax(ParameterInfo info) { if (!info.isAdded() && !info.isTypeNameChanged() && !info.isDeleted()) return null; return TypeContextChecker.checkParameterTypeSyntax(info.getNewTypeName(), fMethod.getJavaProject()); } private RefactoringStatus checkReturnTypeSyntax() { String newTypeName= fReturnTypeInfo.getNewTypeName(); if ("".equals(newTypeName.trim())) { //$NON-NLS-1$ String msg= RefactoringCoreMessages.TypeContextChecker_return_type_not_empty; return RefactoringStatus.createFatalErrorStatus(msg); } List problemsCollector= new ArrayList(0); Type parsedType= parseType(newTypeName, fMethod.getJavaProject(), problemsCollector); if (parsedType == null) { String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_invalid_return_type, BasicElementLabels.getJavaElementName(newTypeName)); return RefactoringStatus.createFatalErrorStatus(msg); } if (problemsCollector.size() == 0) return null; RefactoringStatus result= new RefactoringStatus(); for (Iterator iter= problemsCollector.iterator(); iter.hasNext();) { String[] keys= new String[]{ BasicElementLabels.getJavaElementName(newTypeName), BasicElementLabels.getJavaElementName((String) iter.next())}; String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_invalid_return_type_syntax, keys); result.addError(msg); } return result; } private static boolean isVoidArrayType(Type type){ if (! type.isArrayType()) return false; ArrayType arrayType= (ArrayType)type; if (! arrayType.getComponentType().isPrimitiveType()) return false; PrimitiveType primitiveType= (PrimitiveType)arrayType.getComponentType(); return (primitiveType.getPrimitiveTypeCode() == PrimitiveType.VOID); } } private static Type parseType(String typeString, IJavaProject javaProject, List/*<IProblem>*/ problemsCollector) { if ("".equals(typeString.trim())) //speed up for a common case //$NON-NLS-1$ return null; if (! typeString.trim().equals(typeString)) return null; StringBuffer cuBuff= new StringBuffer(); cuBuff.append("interface A{"); //$NON-NLS-1$ int offset= cuBuff.length(); cuBuff.append(typeString).append(" m();}"); //$NON-NLS-1$ ASTParser p= ASTParser.newParser(AST.JLS3); p.setSource(cuBuff.toString().toCharArray()); p.setProject(javaProject); CompilationUnit cu= (CompilationUnit) p.createAST(null); Selection selection= Selection.createFromStartLength(offset, typeString.length()); SelectionAnalyzer analyzer= new SelectionAnalyzer(selection, false); cu.accept(analyzer); ASTNode selected= analyzer.getFirstSelectedNode(); if (!(selected instanceof Type)) return null; Type type= (Type)selected; if (MethodTypesSyntaxChecker.isVoidArrayType(type)) return null; IProblem[] problems= ASTNodes.getProblems(type, ASTNodes.NODE_ONLY, ASTNodes.PROBLEMS); if (problems.length > 0) { for (int i= 0; i < problems.length; i++) problemsCollector.add(problems[i].getMessage()); } String typeNodeRange= cuBuff.substring(type.getStartPosition(), ASTNodes.getExclusiveEnd(type)); if (typeString.equals(typeNodeRange)) return type; else return null; } private static ITypeBinding handleBug84585(ITypeBinding typeBinding) { if (typeBinding == null) return null; else if (typeBinding.isGenericType() && ! typeBinding.isRawType() && ! typeBinding.isParameterizedType()) return null; //see bug 84585 else return typeBinding; } public static RefactoringStatus[] checkAndResolveMethodTypes(IMethod method, StubTypeContext stubTypeContext, List parameterInfos, ReturnTypeInfo returnTypeInfo) throws CoreException { MethodTypesChecker checker= new MethodTypesChecker(method, stubTypeContext, parameterInfos, returnTypeInfo); return checker.checkAndResolveMethodTypes(); } public static RefactoringStatus[] checkMethodTypesSyntax(IMethod method, List parameterInfos, ReturnTypeInfo returnTypeInfo) { MethodTypesSyntaxChecker checker= new MethodTypesSyntaxChecker(method, parameterInfos, returnTypeInfo); return checker.checkSyntax(); } public static RefactoringStatus checkParameterTypeSyntax(String type, IJavaProject project) { String newTypeName= ParameterInfo.stripEllipsis(type.trim()).trim(); String typeLabel= BasicElementLabels.getJavaElementName(type); if ("".equals(newTypeName.trim())){ //$NON-NLS-1$ String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_parameter_type, typeLabel); return RefactoringStatus.createFatalErrorStatus(msg); } if (ParameterInfo.isVarargs(type) && ! JavaModelUtil.is50OrHigher(project)) { String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_no_vararg_below_50, typeLabel); return RefactoringStatus.createFatalErrorStatus(msg); } List problemsCollector= new ArrayList(0); Type parsedType= parseType(newTypeName, project, problemsCollector); boolean valid= parsedType != null; if (valid && parsedType instanceof PrimitiveType) valid= ! PrimitiveType.VOID.equals(((PrimitiveType) parsedType).getPrimitiveTypeCode()); if (! valid) { String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_invalid_type_name, BasicElementLabels.getJavaElementName(newTypeName)); return RefactoringStatus.createFatalErrorStatus(msg); } if (problemsCollector.size() == 0) return null; RefactoringStatus result= new RefactoringStatus(); for (Iterator iter= problemsCollector.iterator(); iter.hasNext();) { String msg= Messages.format(RefactoringCoreMessages.TypeContextChecker_invalid_type_syntax, new String[]{BasicElementLabels.getJavaElementName(newTypeName), BasicElementLabels.getJavaElementName((String) iter.next())}); result.addError(msg); } return result; } public static StubTypeContext createStubTypeContext(ICompilationUnit cu, CompilationUnit root, int focalPosition) throws CoreException { StringBuffer bufBefore= new StringBuffer(); StringBuffer bufAfter= new StringBuffer(); int introEnd= 0; PackageDeclaration pack= root.getPackage(); if (pack != null) introEnd= pack.getStartPosition() + pack.getLength(); List imports= root.imports(); if (imports.size() > 0) { ImportDeclaration lastImport= (ImportDeclaration) imports.get(imports.size() - 1); introEnd= lastImport.getStartPosition() + lastImport.getLength(); } bufBefore.append(cu.getBuffer().getText(0, introEnd)); fillWithTypeStubs(bufBefore, bufAfter, focalPosition, root.types()); bufBefore.append(' '); bufAfter.insert(0, ' '); return new StubTypeContext(cu, bufBefore.toString(), bufAfter.toString()); } private static void fillWithTypeStubs(final StringBuffer bufBefore, final StringBuffer bufAfter, final int focalPosition, List/*<? extends BodyDeclaration>*/ types) { StringBuffer buf; for (Iterator iter= types.iterator(); iter.hasNext();) { BodyDeclaration bodyDeclaration= (BodyDeclaration) iter.next(); if (! (bodyDeclaration instanceof AbstractTypeDeclaration)) { //account for local classes: if (! (bodyDeclaration instanceof MethodDeclaration)) continue; int bodyStart= bodyDeclaration.getStartPosition(); int bodyEnd= bodyDeclaration.getStartPosition() + bodyDeclaration.getLength(); if (! (bodyStart < focalPosition && focalPosition < bodyEnd)) continue; MethodDeclaration methodDeclaration= (MethodDeclaration) bodyDeclaration; buf= bufBefore; appendModifiers(buf, methodDeclaration.modifiers()); appendTypeParameters(buf, methodDeclaration.typeParameters()); buf.append(" void "); //$NON-NLS-1$ buf.append(methodDeclaration.getName().getIdentifier()); buf.append("(){\n"); //$NON-NLS-1$ Block body= methodDeclaration.getBody(); body.accept(new HierarchicalASTVisitor() { public boolean visit(AbstractTypeDeclaration node) { fillWithTypeStubs(bufBefore, bufAfter, focalPosition, Collections.singletonList(node)); return false; } public boolean visit(ClassInstanceCreation node) { AnonymousClassDeclaration anonDecl= node.getAnonymousClassDeclaration(); if (anonDecl == null) return false; int anonStart= anonDecl.getStartPosition(); int anonEnd= anonDecl.getStartPosition() + anonDecl.getLength(); if (! (anonStart < focalPosition && focalPosition < anonEnd)) return false; bufBefore.append(" new "); //$NON-NLS-1$ bufBefore.append(node.getType().toString()); bufBefore.append("(){\n"); //$NON-NLS-1$ fillWithTypeStubs(bufBefore, bufAfter, focalPosition, anonDecl.bodyDeclarations()); bufAfter.append("};\n"); //$NON-NLS-1$ return false; } }); buf= bufAfter; buf.append("}\n"); //$NON-NLS-1$ continue; } AbstractTypeDeclaration decl= (AbstractTypeDeclaration) bodyDeclaration; buf= decl.getStartPosition() < focalPosition ? bufBefore : bufAfter; appendModifiers(buf, decl.modifiers()); if (decl instanceof TypeDeclaration) { TypeDeclaration type= (TypeDeclaration) decl; buf.append(type.isInterface() ? "interface " : "class "); //$NON-NLS-1$//$NON-NLS-2$ buf.append(type.getName().getIdentifier()); appendTypeParameters(buf, type.typeParameters()); if (type.getSuperclassType() != null) { buf.append(" extends "); //$NON-NLS-1$ buf.append(ASTNodes.asString(type.getSuperclassType())); } List superInterfaces= type.superInterfaceTypes(); appendSuperInterfaces(buf, superInterfaces); } else if (decl instanceof AnnotationTypeDeclaration) { AnnotationTypeDeclaration annotation= (AnnotationTypeDeclaration) decl; buf.append("@interface "); //$NON-NLS-1$ buf.append(annotation.getName().getIdentifier()); } else if (decl instanceof EnumDeclaration) { EnumDeclaration enumDecl= (EnumDeclaration) decl; buf.append("enum "); //$NON-NLS-1$ buf.append(enumDecl.getName().getIdentifier()); List superInterfaces= enumDecl.superInterfaceTypes(); appendSuperInterfaces(buf, superInterfaces); } buf.append("{\n"); //$NON-NLS-1$ if (decl instanceof EnumDeclaration) buf.append(";\n"); //$NON-NLS-1$ fillWithTypeStubs(bufBefore, bufAfter, focalPosition, decl.bodyDeclarations()); buf= decl.getStartPosition() + decl.getLength() < focalPosition ? bufBefore : bufAfter; buf.append("}\n"); //$NON-NLS-1$ } } private static void appendTypeParameters(StringBuffer buf, List typeParameters) { int typeParametersCount= typeParameters.size(); if (typeParametersCount > 0) { buf.append('<'); for (int i= 0; i < typeParametersCount; i++) { TypeParameter typeParameter= (TypeParameter) typeParameters.get(i); buf.append(ASTNodes.asString(typeParameter)); if (i < typeParametersCount - 1) buf.append(','); } buf.append('>'); } } private static void appendModifiers(StringBuffer buf, List modifiers) { for (Iterator iterator= modifiers.iterator(); iterator.hasNext();) { IExtendedModifier extendedModifier= (IExtendedModifier) iterator.next(); if (extendedModifier.isModifier()) { Modifier modifier= (Modifier) extendedModifier; buf.append(modifier.getKeyword().toString()).append(' '); } } } private static void appendSuperInterfaces(StringBuffer buf, List superInterfaces) { int superInterfaceCount= superInterfaces.size(); if (superInterfaceCount > 0) { buf.append(" implements "); //$NON-NLS-1$ for (int i= 0; i < superInterfaceCount; i++) { Type superInterface= (Type) superInterfaces.get(i); buf.append(ASTNodes.asString(superInterface)); if (i < superInterfaceCount - 1) buf.append(','); } } } public static StubTypeContext createSuperInterfaceStubTypeContext(String typeName, IType enclosingType, IPackageFragment packageFragment) { return createSupertypeStubTypeContext(typeName, true, enclosingType, packageFragment); } public static StubTypeContext createSuperClassStubTypeContext(String typeName, IType enclosingType, IPackageFragment packageFragment) { return createSupertypeStubTypeContext(typeName, false, enclosingType, packageFragment); } private static StubTypeContext createSupertypeStubTypeContext(String typeName, boolean isInterface, IType enclosingType, IPackageFragment packageFragment) { StubTypeContext stubTypeContext; String prolog= "class " + typeName + (isInterface ? " implements " : " extends "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ String epilog= " {} "; //$NON-NLS-1$ if (enclosingType != null) { try { ICompilationUnit cu= enclosingType.getCompilationUnit(); ISourceRange typeSourceRange= enclosingType.getSourceRange(); int focalPosition= typeSourceRange.getOffset() + typeSourceRange.getLength() - 1; // before closing brace ASTParser parser= ASTParser.newParser(AST.JLS3); parser.setSource(cu); parser.setFocalPosition(focalPosition); CompilationUnit compilationUnit= (CompilationUnit) parser.createAST(null); stubTypeContext= createStubTypeContext(cu, compilationUnit, focalPosition); stubTypeContext= new StubTypeContext(stubTypeContext.getCuHandle(), stubTypeContext.getBeforeString() + prolog, epilog + stubTypeContext.getAfterString()); } catch (CoreException e) { JavaPlugin.log(e); stubTypeContext= new StubTypeContext(null, null, null); } } else if (packageFragment != null) { ICompilationUnit cu= packageFragment.getCompilationUnit(JavaTypeCompletionProcessor.DUMMY_CU_NAME); stubTypeContext= new StubTypeContext(cu, "package " + packageFragment.getElementName() + ";" + prolog, epilog); //$NON-NLS-1$//$NON-NLS-2$ } else { stubTypeContext= new StubTypeContext(null, null, null); } return stubTypeContext; } public static Type parseSuperClass(String superClass) { return parseSuperType(superClass, false); } public static Type parseSuperInterface(String superInterface) { return parseSuperType(superInterface, true); } private static Type parseSuperType(String superType, boolean isInterface) { if (! superType.trim().equals(superType)) { return null; } StringBuffer cuBuff= new StringBuffer(); if (isInterface) cuBuff.append("class __X__ implements "); //$NON-NLS-1$ else cuBuff.append("class __X__ extends "); //$NON-NLS-1$ int offset= cuBuff.length(); cuBuff.append(superType).append(" {}"); //$NON-NLS-1$ ASTParser p= ASTParser.newParser(AST.JLS3); p.setSource(cuBuff.toString().toCharArray()); Map options= new HashMap(); JavaModelUtil.set50ComplianceOptions(options); p.setCompilerOptions(options); CompilationUnit cu= (CompilationUnit) p.createAST(null); ASTNode selected= NodeFinder.perform(cu, offset, superType.length()); if (selected instanceof Name) selected= selected.getParent(); if (selected.getStartPosition() != offset || selected.getLength() != superType.length() || ! (selected instanceof Type) || selected instanceof PrimitiveType) { return null; } Type type= (Type) selected; String typeNodeRange= cuBuff.substring(type.getStartPosition(), ASTNodes.getExclusiveEnd(type)); if (! superType.equals(typeNodeRange)){ return null; } return type; } public static ITypeBinding resolveSuperClass(String superclass, IType typeHandle, StubTypeContext superClassContext) { StringBuffer cuString= new StringBuffer(); cuString.append(superClassContext.getBeforeString()); cuString.append(superclass); cuString.append(superClassContext.getAfterString()); try { ICompilationUnit wc= typeHandle.getCompilationUnit().getWorkingCopy(new WorkingCopyOwner() {/*subclass*/}, new NullProgressMonitor()); try { wc.getBuffer().setContents(cuString.toString()); CompilationUnit compilationUnit= new RefactoringASTParser(AST.JLS3).parse(wc, true); ASTNode type= NodeFinder.perform(compilationUnit, superClassContext.getBeforeString().length(), superclass.length()); if (type instanceof Type) { return handleBug84585(((Type) type).resolveBinding()); } else if (type instanceof Name) { ASTNode parent= type.getParent(); if (parent instanceof Type) return handleBug84585(((Type) parent).resolveBinding()); } throw new IllegalStateException(); } finally { wc.discardWorkingCopy(); } } catch (JavaModelException e) { return null; } } public static ITypeBinding[] resolveSuperInterfaces(String[] interfaces, IType typeHandle, StubTypeContext superInterfaceContext) { ITypeBinding[] result= new ITypeBinding[interfaces.length]; int[] interfaceOffsets= new int[interfaces.length]; StringBuffer cuString= new StringBuffer(); cuString.append(superInterfaceContext.getBeforeString()); int last= interfaces.length - 1; for (int i= 0; i <= last; i++) { interfaceOffsets[i]= cuString.length(); cuString.append(interfaces[i]); if (i != last) cuString.append(", "); //$NON-NLS-1$ } cuString.append(superInterfaceContext.getAfterString()); try { ICompilationUnit wc= typeHandle.getCompilationUnit().getWorkingCopy(new WorkingCopyOwner() {/*subclass*/}, new NullProgressMonitor()); try { wc.getBuffer().setContents(cuString.toString()); CompilationUnit compilationUnit= new RefactoringASTParser(AST.JLS3).parse(wc, true); for (int i= 0; i <= last; i++) { ASTNode type= NodeFinder.perform(compilationUnit, interfaceOffsets[i], interfaces[i].length()); if (type instanceof Type) { result[i]= handleBug84585(((Type) type).resolveBinding()); } else if (type instanceof Name) { ASTNode parent= type.getParent(); if (parent instanceof Type) { result[i]= handleBug84585(((Type) parent).resolveBinding()); } else { throw new IllegalStateException(); } } else { throw new IllegalStateException(); } } } finally { wc.discardWorkingCopy(); } } catch (JavaModelException e) { // won't happen } return result; } }